﻿using System;
using System.Collections.Generic;
using System.IO;
using GameStateManagement;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;
using SuperRyoninRPG.Characters;
using SuperRyoninRPG.Characters.Enemys;
using SuperRyoninRPG.Characters.Items;
using SuperRyoninRPG.Controller;
using SuperRyoninRPG.Display;
using SuperRyoninRPG.Main.Tiles;

namespace SuperRyoninRPG.Main
{
	public class Stage
	{
		// レベルの物理的構造。
		// その上にエンティティが描画されるレイヤー。
		private const int EntityLayer = 2;
		private const int PointsPerSecond = 5;
		private static readonly Point InvalidPosition = new Point(-1, -1);

		private List<Comment> showingComments = new List<Comment>();

		public bool IsCommenting { get { return showingComments.Count != 0; } }

		private List<string> showedCommentList = new List<string>();

		// レベル内のエンティティ。
		private readonly List<Coin> coins = new List<Coin>();
		private readonly ContentManager content;
		//private readonly List<Enemy> enemies = new List<Enemy>();
		private readonly List<Enemy> enemies = new List<Enemy>();
		private readonly List<Item> items = new List<Item>();
		private readonly SoundEffect exitReachedSound;
		private readonly Texture2D[] layers;

		// レベル内の主要な位置。

		// レベルのゲームの状態。
		private readonly Random random = new Random(354668); // 任意、ただし一定のシード
		private Point exit = InvalidPosition;
		private Player player;
		private bool reachedExit;

		public bool IsPause { get; set; }

		private int score;
		private Vector2 start;
		internal Tile[,] Tiles;
		private TimeSpan timeRemaining;
		private Song overworldBgm;
		private Texture2D background;
		private Texture2D black;

		public ScreenManager ScreenManager { get; set; }

		private bool aButtonUpped = false;

		public float StagePosision { get; private set; }
		private const float GoAheadPoint = 192f;


		public float DisplayPosition
		{
			get { return this.Player.Position.X - this.StagePosision; }
		}
		#region Loading

		/// <summary>
		///     新しいレベルを構築します。
		/// </summary>
		/// <param name="serviceProvider">
		///     ContentManager を構築するために使用されるサービス プロバイダー。
		/// </param>
		/// <param name="path">
		///     読み込まれるレベル ファイルへの絶対パス。
		/// </param>
		public Stage(ScreenManager screenManager, Stream fileStream, int levelIndex)
		{
			this.ScreenManager = screenManager;
			// 新しいコンテンツ マネージャーを作成し、このレベルによって使用される
			// コンテンツを読み込みます。
			if (this.content == null)
			{
				IServiceProvider serviceProvider = screenManager.Game.Services;
				this.content = new ContentManager(serviceProvider, "Content");
			}
			this.timeRemaining = TimeSpan.FromMinutes(2.0);

			this.LoadTiles(fileStream);

			//// バックグラウンド レイヤーのテクスチャーを読み込みます。
			//// ここでは、すべてのレベルが同じ背景を使用し、これらの左端の部分のみ
			//// 使用する必要があります。
			//this.layers = new Texture2D[3];
			//for (int i = 0; i < this.layers.Length; ++i)
			//{
			//    // レベルの種類の各バックグラウンド レイヤーの場合、ランダムな
			//    // セグメントを選択します。
			//    int segmentIndex = levelIndex;
			//    this.layers[i] =
			//        this.Content.Load<Texture2D>("Images/Backgrounds/Layer" + i + "_" + segmentIndex);
			//}
			this.background = this.Content.Load<Texture2D>("Images/Backgrounds/background");

			this.black = this.Content.Load<Texture2D>("Images/Black");

			// サウンドを
			// 読み込みます。
			this.exitReachedSound = this.Content.Load<SoundEffect>("Sounds/StageClear");
			this.overworldBgm = this.Content.Load<Song>("Sounds/Overworld");
			MediaPlayer.IsRepeating = true;
			MediaPlayer.Play(this.overworldBgm);
		}

		public void AddItem(Item item)
		{
			this.items.Add(item);
			item.Appearance = true;
		}

		/// <summary>
		///     構造体ファイルのすべてのタイルを反復処理し、その外観と動作を
		///     読み込みます。このメソッドは、ファイルがプレイヤーの開始位置、
		///     終了などを含む、適切な形式であることも検証します。
		/// </summary>
		/// <param name="path">
		///     読み込まれるレベル ファイルへの絶対パス。
		/// </param>
		private void LoadTiles(Stream fileStream)
		{
			// レベルを読み込み、すべての行が同じ長さであることを確認します。
			int width;
			var lines = new List<string>();
			using (var reader = new StreamReader(fileStream))
			{
				string line = reader.ReadLine();
				width = line.Length;
				while (line != null)
				{
					lines.Add(line);
					if (line.Length != width)
					{
						throw new Exception(
							String.Format("The length of line {0} is different from all preceeding lines.", lines.Count));
					}
					line = reader.ReadLine();
				}
			}

			// タイル グリッドを
			// 割り当てます。
			this.Tiles = new Tile[width, lines.Count];

			// すべてのタイル位置をループ処理し、
			for (int y = 0; y < this.Height; ++y)
			{
				for (int x = 0; x < this.Width; ++x)
				{
					// 各タイルを読み込みます。
					char tileType = lines[y][x];
					this.Tiles[x, y] = this.LoadTile(tileType, x, y);
				}
			}

			// レベルに開始と終了があることを確認します。
			if (this.Player == null)
			{
				throw new NotSupportedException("A level must have a starting point.");
			}
			if (this.exit == InvalidPosition)
			{
				throw new NotSupportedException("A level must have an exit.");
			}
		}

		/// <summary>
		///     個別のタイルの外観と動作を読み込みます。
		/// </summary>
		/// <param name="tileType">
		///     読み込む必要があるものを指定する構造体ファイルから
		///     読み込まれたキャラクター。
		/// </param>
		/// <param name="x">
		///     タイル空間内のこのタイルの X 位置。
		/// </param>
		/// <param name="y">
		///     タイル空間内のこのタイルの Y 位置。
		/// </param>
		/// <returns>読み込まれるタイル。</returns>
		private Tile LoadTile(char tileType, int x, int y)
		{
			Comment comment;
			Comment[] talks;
			switch (tileType)
			{
				// 空白スペース
				case '.':
					return new Tile(TileCollision.Passable, this, x, y);

				// 終了
				case 'X':
					return this.LoadExitTile(x, y);

				// 宝石
				//case 'G':
				//    return this.LoadGemTile(x, y);

				// 浮かんでいる
				// プラットフォーム
				case '-':
					return this.LoadTile("Platform", TileCollision.Platform, x, y);

				// さまざまな敵
				case 'g':
					comment = new Comment(this.ScreenManager, "ガッツ", "だいがくのこうはい\n\r  ふとい");
					talks = new Comment[] { new Comment(this.ScreenManager, "ガッツ", "「おかしいだろ！！」") };
					return this.LoadEnemyTile(x, y, "Guts", 64, comment, true, talks);
				case 'p':
					comment = new Comment(this.ScreenManager, "ピーロン", "じもとのともだち\n\r  もじゃもじゃ");

					return this.LoadEnemyTile(x, y, "PRon", 32, comment);
				case 'h':
					comment = new Comment(this.ScreenManager, "ほしのさん", "かいしゃのせんぱい\n\r  おさけがだいすき");
					talks = new Comment[] {
						new Comment(this.ScreenManager, "ほしのさん", "「いっぱいのむと つよくなるよ」"),
						new Comment(this.ScreenManager, "", "おさけののみかた をまなんだ")
					};
					return this.LoadEnemyTile(x, y, "Hoshino", 32, comment);
				case 't':
					comment = new Comment(this.ScreenManager, "たっちゃん", "だいがくのともだち\n\r  ちっさいおじさん");
					talks = new Comment[] {
						new Comment(this.ScreenManager, "たっちゃん", "「ちいさくねーわ」"),
						new Comment(this.ScreenManager, "", "めげないこころ をまなんだ")
					};
					return this.LoadEnemyTile(x, y, "Tatsuya", 32, comment, true, talks);
				case 'c':
					comment = new Comment(this.ScreenManager, "まさひろ", "だいがくのこうはい\n\r  いろんないみでちいさい");
					talks = new Comment[] {
						new Comment(this.ScreenManager, "まさひろ", "へんじがない ただのしかばねのようだ"),
					};
					return this.LoadEnemyTile(x, y, "Masahiro", 32, comment, true, talks);
				case 'n':
					comment = new Comment(this.ScreenManager, "なかがわさん", "かいしゃのせんぱい\n\r  キッチリしてる");
					talks = new Comment[] {
						new Comment(this.ScreenManager, "なかがわさん", "「ちょっと デスクが きたないんじゃない？」"),
						new Comment(this.ScreenManager, "", "そうじのしかた をまなんだ")
					};
					return this.LoadEnemyTile(x, y, "Nakagawa", 32, comment, true, talks);
				case 'f':
					comment = new Comment(this.ScreenManager, "まさひで", "あかのたにん\n\r  カオスのしょゆうぶつ");
					talks = new Comment[] {
						new Comment(this.ScreenManager, "まさひで", "「さおりいいいぃぃぃ！！」"),
						new Comment(this.ScreenManager, "", "とくになにも まなべなかった")
					};
					return this.LoadEnemyTile(x, y, "Masahide", 32, comment, true, talks);
				case 'y':
					comment = new Comment(this.ScreenManager, "ようちゃん", "だいがくのともだち\n\r  げんきいっぱい");
					talks = new Comment[] {
						new Comment(this.ScreenManager, "ようちゃん", "「」"),
						new Comment(this.ScreenManager, "", "いきおい をまなんだ")
					};
					return this.LoadEnemyTile(x, y, "Yo", 32, comment, true, talks);
				case 'k':
					comment = new Comment(this.ScreenManager, "けいこちゃん", "りょうすけのはは\n\r  やさしい");
					talks = new Comment[] {
						new Comment(this.ScreenManager, "けいこちゃん", "「あんまりむりするんじゃ ないですよ」"),
						new Comment(this.ScreenManager, "", "やさしさ をまなんだ")
					};
					return this.LoadEnemyTile(x, y, "Keiko", 32, comment, true, talks);
				case 'm':
					comment = new Comment(this.ScreenManager, "みのるさん", "りょうすけのちち\n\r  はげしい");
					talks = new Comment[] {
						new Comment(this.ScreenManager, "みのるさん", "「おれはねるぞー！！」"),
						new Comment(this.ScreenManager, "", "さまざまな ちしき をまなんだ")
					};
					return this.LoadEnemyTile(x, y, "Minoru", 32, comment, true, talks);
				case 'M':
					comment = new Comment(this.ScreenManager, "みちこさん", "かづみのはは\n\r  やさしい");
					talks = new Comment[] {
						new Comment(this.ScreenManager, "みちこさん", "「あんまりむりするんじゃ ないですよ」"),
						new Comment(this.ScreenManager, "", "やさしさ をまなんだ")
					};
					return this.LoadEnemyTile(x, y, "Michiko", 32, comment, true, talks);
				case 'T':
					comment = new Comment(this.ScreenManager, "たけしさん", "かづみのちち\n\r  はげしい");
					talks = new Comment[] {
						new Comment(this.ScreenManager, "たけしさん", "「おれはねるぞー！！」"),
						new Comment(this.ScreenManager, "", "さまざまな ちしき をまなんだ")
					};
					return this.LoadEnemyTile(x, y, "Takeshi", 32, comment, true, talks);

				// プラットフォームの
				// ブロック
				case '~':
					return this.LoadVarietyTile("BlockB", 2, TileCollision.Platform, x, y);

				// 通行できる
				// ブロック
				case ':':
					return this.LoadVarietyTile("BlockB", 2, TileCollision.Passable, x, y);

				// プレイヤー 1 の開始点
				case 'S':
					return this.LoadStartTile(x, y);

				// 通行できないブロック
				case '#'://地面
					return this.LoadTile("Ground", TileCollision.Impassable, x, y);
				case 'B'://ブロック
					return this.LoadTile("Block", TileCollision.Breakable, x, y);
				case '+':
					return this.LoadTile("Stone", TileCollision.Impassable, x, y);
				case '?':
					return this.LoadAnimationTile("ItemBlock", TileCollision.Impassable, x, y);
				case 'x'://タキシード
					Vector2 position = this.GetBounds(x, y - 1).GetBottomCenter();

					var mashroom = new Mashroom(this, position, 32, "MashRoom");
					return this.LoadItemBlockTile<Mashroom>("ItemBlock", TileCollision.Impassable, x, y, mashroom);
				case 'R'://土管右上
					return this.LoadTile("PipeRightTop", TileCollision.Impassable, x, y);
				case 'r'://土管右下
					return this.LoadTile("PipeRightBottom", TileCollision.Impassable, x, y);
				case 'L'://土管左上
					return this.LoadTile("PipeLeftTop", TileCollision.Impassable, x, y);
				case 'l'://土管左下
					return this.LoadTile("PipeLeftBottom", TileCollision.Impassable, x, y);
				//旗
				case '/':
					return this.LoadTile("FlagTopLeft", TileCollision.Passable, x, y);
				case '\\':
					return this.LoadTile("FlagBottomLeft", TileCollision.Passable, x, y);
				case ']':
					return this.LoadTile("FlagTopRight", TileCollision.Passable, x, y);
				case '"':
					return this.LoadTile("FlagBottomRight", TileCollision.Passable, x, y);
				case '|':
					return this.LoadTile("Bar", TileCollision.Passable, x, y);
				// 不明なタイル型のキャラクター
				default:
					throw new NotSupportedException(
						String.Format("Unsupported tile type character '{0}' at position {1}, {2}.", tileType, x, y));
			}
		}

		/// <summary>
		///     新しいタイルを作成します。通常、その他のタイル読み込みメソッドは、
		///     特殊なロジックを実行後、このメソッドにチェーンされます。
		/// </summary>
		/// <param name="name">
		///     Content/Tiles ディレクトリを基準にしたタイル テクスチャーへのパス。
		/// </param>
		/// <param name="collision">
		///     新しいタイルのタイル衝突型。
		/// </param>
		/// <returns>新しいタイル。</returns>
		private Tile LoadItemBlockTile<TItem>(string name, TileCollision collision, int x, int y, TItem item) where TItem : Item
		{

			return new ItemBlock<TItem>(name, collision, this, x, y, 32, item);
		}

		/// <summary>
		///     新しいタイルを作成します。通常、その他のタイル読み込みメソッドは、
		///     特殊なロジックを実行後、このメソッドにチェーンされます。
		/// </summary>
		/// <param name="name">
		///     Content/Tiles ディレクトリを基準にしたタイル テクスチャーへのパス。
		/// </param>
		/// <param name="collision">
		///     新しいタイルのタイル衝突型。
		/// </param>
		/// <returns>新しいタイル。</returns>
		private Tile LoadTile(string name, TileCollision collision, int x, int y)
		{
			return new Tile(this.Content.Load<Texture2D>("Images/Tiles/" + name), collision, this, x, y);
		}

		private Tile LoadAnimationTile(string name, TileCollision collision, int x, int y)
		{
			Animation animation = new Animation(this.Content.Load<Texture2D>("Images/Tiles/" + name), Tile.Width,
				0.15f, true);
			AnimationPlayer animationPlayer = new AnimationPlayer();
			animationPlayer.PlayAnimation(animation);
			return new Tile(animation, collision, this, x, y);
		}

		/// <summary>
		///     ランダムな外観を持つタイルを読み込みます。
		/// </summary>
		/// <param name="baseName">
		///     このタイル バリエーションのグループのコンテンツ名のプレフィックス。
		///     タイル グループは、LikeThis0.png、LikeThis1.png、LikeThis2.png などのように
		///     命名されます。
		/// </param>
		/// <param name="variationCount">
		///     このグループに含まれるバリエーションの数。
		/// </param>
		private Tile LoadVarietyTile(string baseName, int variationCount, TileCollision collision, int x, int y)
		{
			int index = this.random.Next(variationCount);
			return this.LoadTile(baseName + index, collision, x, y);
		}

		/// <summary>
		///     プレイヤーをインスタンス化し、レベル内に配置し、復活したときに配置した場所を思い出します。
		/// </summary>
		private Tile LoadStartTile(int x, int y)
		{
			if (this.Player != null)
			{
				throw new NotSupportedException("A level may only have one starting point.");
			}

			this.start = this.GetBounds(x, y).GetBottomCenter();
			this.player = new Player(this, this.start);
			this.player.Appearance = true;
			this.player.Bumped += new EventHandler<Thing.BumpedEventArgs>(Player_Bumped);
			return new Tile(TileCollision.Passable, this, x, y);
		}

		void Player_Bumped(object sender, Thing.BumpedEventArgs e)
		{
			e.Tile.Bumped();
		}

		/// <summary>
		///     レベルの終了位置を記憶します。
		/// </summary>
		private Tile LoadExitTile(int x, int y)
		{
			if (this.exit != InvalidPosition)
			{
				throw new NotSupportedException("A level may only have one exit.");
			}

			this.exit = this.GetBounds(x, y).Center;

			return this.LoadTile("Exit", TileCollision.Passable, x, y);
		}

		/// <summary>
		///     敵をインスタンス化し、レベル内に配置します。
		/// </summary>
		//private Tile LoadEnemyTile(int x, int y, string spriteSet)
		private Tile LoadEnemyTile(int x, int y, string name, int frameWidth, Comment comment,bool killAble = false,Comment[] talks = null)
		{
			Vector2 position = this.GetBounds(x, y).GetBottomCenter();
			var enemy = new Enemy(this, position, MoveType.BoundedRandom, name, frameWidth)
			{
				Comment = comment,
				Talks = talks,
				IsKillable = killAble
			};
			this.enemies.Add(enemy);
			//this.enemies.Add(new Enemy(this, position, spriteSet));

			return new Tile(TileCollision.Passable, this, x, y);
		}

		/// <summary>
		///     宝石をインスタンス化し、レベル内に配置します。
		/// </summary>
		private Tile LoadGemTile(int x, int y)
		{
			Point position = this.GetBounds(x, y).Center;
			this.coins.Add(new Coin(this, new Vector2(position.X, position.Y)));

			return new Tile(TileCollision.Passable, this, x, y);
		}

		/// <summary>
		///     レベル コンテンツを
		///     アンロードします。
		/// </summary>
		public void Dispose()
		{
			this.Content.Unload();
		}

		#endregion

		#region Bounds and collision

		/// <summary>
		///     タイル内で測定されたレベルの幅。
		/// </summary>
		public int Width
		{
			get { return this.Tiles.GetLength(0); }
		}

		/// <summary>
		///     タイル内で測定されたレベルの高さ。
		/// </summary>
		public int Height
		{
			get { return this.Tiles.GetLength(1); }
		}

		/// <summary>
		///     タイルの衝突モードを特定の位置で取得します。
		///     このメソッドは、左右の端部を通過して逃走できないようにするが、
		///     レベルの上部を飛び越え、下部から落下できるようにすることで、
		///     レベルの境界外にあるタイルを扱います。
		/// </summary>
		public TileCollision GetCollision(int x, int y)
		{
			// レベルの終了点を通って逃走できない
			// ようにします。
			if (x < 0 || x >= this.Width)
			{
				return TileCollision.Impassable;
			}
			// レベルの上部を飛び越え、下部から落下できるようにします。
			if (y < 0 || y >= this.Height)
			{
				return TileCollision.Passable;
			}

			return this.Tiles[x, y].Collision;
		}

		protected void OnColision(int x, int y)
		{
			if (Collision != null)
			{
				var args = new CollisionArgs()
				{
					X = x,
					Y = y
				};
				this.Collision(this, args);
			}
		}

		public event EventHandler<CollisionArgs> Collision;
		public class CollisionArgs : EventArgs
		{
			public TileCollision Collision { get; set; }
			public int X { get; set; }
			public int Y { get; set; }
		}

		/// <summary>
		///     ワールド空間内のタイルの境界矩形を取得します。
		/// </summary>
		public Rectangle GetBounds(int x, int y)
		{
			return new Rectangle(x * Tile.Width, y * Tile.Height, Tile.Width, Tile.Height);
		}

		#endregion

		#region Update

		private float fadeOutAlpha = 0;
		private float fadeOutTime = 0f;
		private float nextFadeOutTime = 0.1f;
		/// <summary>
		///     ワールド内のすべてのオブジェクトを更新し、オブジェクト間の衝突を実行し、
		///     得点方式により制限時間を処理します。
		/// </summary>
		public void Update(
			GameTime gameTime,
			KeyboardState keyboardState,
			GamePadState gamePadState,
			TouchCollection touchState,
			AccelerometerState accelState,
			DisplayOrientation orientation)
		{
			if (this.IsPause)
			{
				return;
			}

			if (reachedExit)
			{
				UpdateExit(gameTime);
				return;
			}

			if (this.IsCommenting)
			{
				bool keyUp = gamePadState.IsButtonUp(Buttons.A) &&
						 keyboardState.IsKeyUp(Keys.Space) &&
						 keyboardState.IsKeyUp(Keys.Z);
				if (this.IsCommenting)
				{
					this.showingComments[0].Update(gameTime, keyboardState, gamePadState, touchState, accelState, orientation);
				}
				if (!showingComments[0].IsKeyUp)
				{
					showingComments[0].IsKeyUp = keyUp;
					return;
				}
				else if (!keyUp)
				{
					this.showingComments.RemoveAt(0);
				}



				return;
			}
			// プレイヤーが死亡したか、時間切れになったときに
			// 一時停止します。
			//if (!this.Player.IsAlive || this.TimeRemaining == TimeSpan.Zero)
			if (!this.Player.IsAlive)
			{
				// プレイヤー上で引き続き物理処理を実行する
				// 必要があります。
				this.Player.ApplyPhysics(gameTime);
				this.UpdateEnemies(gameTime, keyboardState, gamePadState, touchState, accelState, orientation);
			}
			else
			{
				this.timeRemaining -= gameTime.ElapsedGameTime;
				this.Player.Update(gameTime, keyboardState, gamePadState, touchState, accelState, orientation);

				this.UpdateGems(gameTime);
				if (this.DisplayPosition > GoAheadPoint)
				{
					this.GoAhead(this.DisplayPosition - GoAheadPoint);
				}
				// レベルの下部から落下させてプレイヤーを倒します。
				if (this.Player.BoundingRectangle.Top >= this.Height * Tile.Height)
				{
					this.OnPlayerKilled(null);
				}

				this.UpdateEnemies(gameTime, keyboardState, gamePadState, touchState, accelState, orientation);
				UpdateTiles(gameTime);
				UpdateItems(gameTime, keyboardState, gamePadState, touchState, accelState, orientation);
				// プレイヤーが地面の上に立ち、境界矩形が終了タイルの中央に含まれる場合、
				// プレイヤーは出口に到達しています。プレイヤーは、すべての宝石を
				// 収集した場合にのみ終了することができます。
				if (this.Player.IsAlive &&
					this.Player.IsOnGround &&
					this.Player.BoundingRectangle.Contains(this.exit))
				{
					this.OnExitReached();
				}
			}

			// 残存時間をゼロにクランプします。
			if (this.timeRemaining < TimeSpan.Zero)
			{
				this.timeRemaining = TimeSpan.Zero;
			}

			UpdateTalk(gameTime, keyboardState, gamePadState, touchState, accelState, orientation);
		}

		private void UpdateExit(GameTime gameTime)
		{

			// ポイントに変換されている時間をアニメーション
			// 表示します。
			//var seconds = (int) Math.Round(gameTime.ElapsedGameTime.TotalSeconds * 100.0f);
			//seconds = Math.Min(seconds, (int) Math.Ceiling(this.TimeRemaining.TotalSeconds));
			//this.timeRemaining -= TimeSpan.FromSeconds(seconds);
			//this.score += seconds * PointsPerSecond;
			var elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
			fadeOutTime += elapsed;
			if (fadeOutTime > nextFadeOutTime)
			{
				fadeOutAlpha += 0.01f;
				if (fadeOutAlpha > 1.0f)
				{
					// 現在のすべての画面をオフに移行するように指示します。
					foreach (GameScreen screen in this.ScreenManager.GetScreens())
					{
						screen.ExitScreen();
					}

					// ローディング画面を作成し、アクティブ化します。
					this.ScreenManager.AddScreen(new EndingScreen(), null);
				}

			}
		}

		/// <summary>
		///     個々の宝石をアニメーション表示し、プレイヤーがこれらを収集できるか
		///     どうか確認します。
		/// </summary>
		private void UpdateGems(GameTime gameTime)
		{
			for (int i = 0; i < this.coins.Count; ++i)
			{
				Coin coin = this.coins[i];

				coin.Update(gameTime);

				if (coin.BoundingCircle.Intersects(this.Player.BoundingRectangle))
				{
					this.coins.RemoveAt(i--);
					this.OnGemCollected(coin, this.Player);
				}
			}
		}

		/// <summary>
		///     個々の宝石をアニメーション表示し、プレイヤーがこれらを収集できるか
		///     どうか確認します。
		/// </summary>
		private void UpdateTiles(GameTime gameTime)
		{
			foreach (var tile in Tiles)
			{
				tile.Update(gameTime);
			}
		}

		private void UpdateItems(
			GameTime gameTime,
			KeyboardState keyboardState,
			GamePadState gamePadState,
			TouchCollection touchState,
			AccelerometerState accelState,
			DisplayOrientation orientation)
		{
			var collisionedItem = new List<Item>();
			foreach (var item in this.items)
			{
				item.Update(gameTime, keyboardState, gamePadState, touchState, accelState, orientation);
				if (this.player.IsAlive && item.BoundingRectangle.Intersects(this.Player.BoundingRectangle))
				{
					this.OnPlayerGetItem(item);
					collisionedItem.Add(item);
				}
			}

			foreach (var item in collisionedItem)
			{
				this.items.Remove(item);
			}
		}
		private const Buttons DushButton = Buttons.B;
		private void UpdateTalk(GameTime gameTime,
			KeyboardState keyboardState,
			GamePadState gamePadState,
			TouchCollection touchState,
			AccelerometerState accelState,
			DisplayOrientation orientation)
		{
			bool pressing=
				gamePadState.IsButtonDown(DushButton) ||
				keyboardState.IsKeyDown(Keys.Z) ||
				touchState.AnyTouch();
			bool lastPressed = aButtonUpped;
			aButtonUpped = pressing;
			if (lastPressed || !pressing)
			{
				return;
			}

			float talkSize = 1.0f;

			Rectangle bounds = this.Player.BoundingRectangle;
			int x = (int)(bounds.X - bounds.Width * talkSize / 2.0f);
			int y = (int)(bounds.Y - bounds.Height * talkSize / 2.0f);
			int width = (int)(bounds.Width  + bounds.Width * talkSize);
			int height = (int)(bounds.Height  + bounds.Height* talkSize);
			Rectangle talkRectangle = new Rectangle(x, y, width, height);
			foreach (Enemy enemy in this.enemies)
			{
				if (this.player.IsAlive && enemy.IsAlive 
					&& !this.player.Invincible 
					&& enemy.CanTalk &&
					enemy.BoundingRectangle.Intersects(talkRectangle))
				{
					this.showingComments.AddRange(enemy.Talks);
				}
			}
		}


		private void OnPlayerGetItem(Item item)
		{
			this.Player.GetItem(item);
		}

		/// <summary>
		///     個々の敵をアニメーション表示し、敵がプレイヤーを倒せる
		///     ようにします。
		/// </summary>
		private void UpdateEnemies(GameTime gameTime,
			KeyboardState keyboardState,
			GamePadState gamePadState,
			TouchCollection touchState,
			AccelerometerState accelState,
			DisplayOrientation orientation)
		{
			foreach (Enemy enemy in this.enemies)
			{
				enemy.Update(gameTime, keyboardState, gamePadState, touchState, accelState, orientation);

				// 敵に触れた途端にプレイヤーが倒されます。
				if (this.player.IsAlive && enemy.IsAlive && !this.player.Invincible && 
					enemy.BoundingRectangle.Intersects(this.Player.BoundingRectangle))
				{
					if (enemy.DieType != DieType.None)
					{
						enemy.OnDamaged(player);
					}
					//this.OnPlayerKilled(enemy);
				}
			}
		}

		/// <summary>
		///     宝石が収集されたときに
		///     呼び出されます。
		/// </summary>
		/// <param name="coin">収集された宝石。</param>
		/// <param name="collectedBy">この宝石を収集したプレイヤー。</param>
		private void OnGemCollected(Coin coin, Player collectedBy)
		{
			this.score += Coin.PointValue;

			coin.OnCollected(collectedBy);
		}

		/// <summary>
		///     プレイヤーが倒されたときに
		///     呼び出されます。
		/// </summary>
		/// <param name="killedBy">
		///     プレイヤーを倒した敵。これは、プレイヤーが穴に落ちた場合など、
		///     敵に倒されなかった場合 null です。
		/// </param>
		private void OnPlayerKilled(Character killedBy)
		{
			this.Player.OnDamaged(killedBy);
		}

		/// <summary>
		///     プレイヤーがレベルの出口に到達したときに
		///     呼び出されます。
		/// </summary>
		private void OnExitReached()
		{
			MediaPlayer.Stop();


			this.Player.OnReachedExit();
			//this.exitReachedSound.Play();
			this.reachedExit = true;
		}

		/// <summary>
		///     レベルをもう一度試みるために、プレイヤーを開始点に復元します。
		/// </summary>
		public void StartNewLife()
		{
			this.Player.Reset(this.start);
		}

		#endregion

		#region Draw



		/// <summary>
		///     背景から前景まで、レベル内のすべてを描画します。
		/// </summary>
		public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
		{
			int height = this.ScreenManager.GraphicsDevice.PresentationParameters.BackBufferHeight;
			var scale = (float)height / background.Height;
			var backgroundRectangle = new Rectangle(-((int)(StagePosision / 2)), 0, (int)(background.Width * scale), (int)(background.Height * scale));
			spriteBatch.Draw(background, backgroundRectangle, Color.White);

			//for (int i = 0; i <= EntityLayer; ++i)
			//{
			//    spriteBatch.Draw(this.layers[i], Vector2.Zero, Color.White);
			//}
			foreach (var item in items)
			{
				item.Draw(gameTime, spriteBatch);
			}
			this.DrawTiles(gameTime, spriteBatch);

			foreach (Coin gem in this.coins)
			{
				gem.Draw(gameTime, spriteBatch);
			}

			if (this.IsCommenting)
			{
				Mask(spriteBatch, 0.7f);
			}
			foreach (Enemy enemy in this.enemies)
			{
				enemy.Draw(gameTime, spriteBatch);
			}
			this.Player.Draw(gameTime, spriteBatch);

			//foreach (Enemy enemy in this.enemies)
			

			if (this.IsCommenting)
			{
				this.showingComments[0].Draw(gameTime, spriteBatch);
			}

			//if (reachedExit)
			//{
			Mask(spriteBatch, fadeOutAlpha);
			//}

			//for (int i = EntityLayer + 1; i < this.layers.Length; ++i)
			//{

			//    spriteBatch.Draw(this.layers[i], Vector2.Zero, Color.White);
			//}
		}

		private void Mask(SpriteBatch spriteBatch, float alpha)
		{
			Viewport viewport = this.ScreenManager.GraphicsDevice.Viewport;
			var fullscreen = new Rectangle(0, 0, viewport.Width, viewport.Height);
			var color = new Color(0, 0, 0, alpha);
			spriteBatch.Draw(this.black, fullscreen, color);
		}

		/// <summary>
		///     レベル内の各タイルを描画します。
		/// </summary>
		private void DrawTiles(GameTime gameTime, SpriteBatch spriteBatch)
		{
			// タイルの位置に対する
			// 実行を繰り返す
			for (int y = 0; y < this.Height; ++y)
			{
				for (int x = 0; x < this.Width; ++x)
				{
					// その位置に表示可能なタイルが存在する場合
					Texture2D texture = this.Tiles[x, y].Texture;
					if (texture != null)
					{
						// 画面のスペースに描画します。
						Vector2 position = new Vector2(x, y) * Tile.Size;
						var rectangle = new Rectangle((int)position.X - (int)StagePosision, (int)position.Y, Tile.Width, Tile.Width);
						//spriteBatch.Draw(texture, rectangle,null, Color.White);
						this.Tiles[x, y].Draw(gameTime, spriteBatch);
					}
				}
			}
		}

		public void GoAhead(float x)
		{
			StagePosision += x;
			AppearanceEnemies();
		}

		public void AppearanceEnemies()
		{
			if (IsCommenting)
			{
				return;
			}

			foreach (var enemy in enemies)
			{
				if (!enemy.Appearance)
				{
					enemy.Appearance = IsInterSect(enemy);
				}
				if (!enemy.Commented)
				{
					bool contains = IsContains(enemy);
					if (contains)
					{
						if (enemy.Comment != null
							&& !showedCommentList.Contains(enemy.Comment.Name))
						{
							showedCommentList.Add(enemy.Comment.Name);
							enemy.Commented = true;
							this.showingComments.Add(enemy.Comment);
						}
					}
				}
			}
		}

		public bool IsInterSect(Thing thing)
		{
			var parameters = this.ScreenManager.GraphicsDevice.PresentationParameters;
			var display = new Rectangle((int)this.StagePosision, 0, (int)this.ScreenManager.ScreenWidth,
				(int)this.ScreenManager.ScreenHeight);
			bool intersects = thing.BoundingRectangle.Intersects(display);

			return intersects;
		}

		public bool IsContains(Thing thing)
		{
			var parameters = this.ScreenManager.GraphicsDevice.PresentationParameters;
			var display = new Rectangle((int)this.StagePosision, 0, (int)this.ScreenManager.ScreenWidth,
				(int)this.ScreenManager.ScreenHeight);

			bool contains = display.Contains(thing.BoundingRectangle);

			return contains;
		}

		#endregion

		public Player Player
		{
			get { return this.player; }
		}

		public int Score
		{
			get { return this.score; }
		}

		public bool ReachedExit
		{
			get { return this.reachedExit; }
		}

		public TimeSpan TimeRemaining
		{
			get { return this.timeRemaining; }
		}

		// レベル コンテンツ。
		public ContentManager Content
		{
			get { return this.content; }
		}
	}
}